﻿using MikuMikuLibrary.Databases;
using MikuMikuModel.FarcPack;
using MikuMikuModel.Logs;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static ft_module_parser.aftconversion.divax.pvparam_lighttest;

namespace ft_module_parser.aftconversion.divax
{
    class mass_convert
    {
        ObjectDatabase objdb = new ObjectDatabase();
        ObjectDatabase objdb2 = new ObjectDatabase();
        objset divax = new objset();
        StageDatabase staged = new StageDatabase();
        StageDatabase staged2 = new StageDatabase();
        a3ds auth3d_db = new a3ds();
        divamodgen divamods = new divamodgen();
        ConcurrentStack<models> model_list = new ConcurrentStack<models>();
        ConcurrentStack<PVPPTransition> pvpp = new ConcurrentStack<PVPPTransition>();
        public static int currentWorkerA3DA = 0;
        public static int currentWorker = 0;
        public static int maxWorker = 4;

        public static int filescount = 0;
        public void Convert(string path, string objdbpath, string texdbpath, string stagedbpath, string a3ddbpath, string mot_db, string acpath, bool skipmot = false, bool skippvparam = false, bool mdatatest = true)
        {
            maxWorker = Environment.ProcessorCount + 1;

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            Console.Title = "LYB DIVA X2AFT";
            Console.WriteLine("lyb's Diva X to AFT Conversion Utility");
            Console.WriteLine(textconst.credits);
            Console.WriteLine("");
            
            divapvmod divapvmods = new divapvmod();
            //divapvmods.RestoreDb(true);

            if (Directory.Exists("a3d"))
                Directory.Delete("a3d", true);
            if (Directory.Exists("farc"))
                Directory.Delete("farc", true);
            if (Directory.Exists("temp"))
                Directory.Delete("temp", true);
            if (Directory.Exists("dsc"))
                Directory.Delete("dsc", true);
            if (Directory.Exists("mot"))
                Directory.Delete("mot", true);

            if (!Directory.Exists("x_obj_cache"))
                Directory.CreateDirectory("x_obj_cache");

            Directory.CreateDirectory("temp");
            Directory.CreateDirectory("farc");
            Directory.CreateDirectory("dsc");
            Directory.CreateDirectory("farc");

            if (!Directory.Exists(acpath + "\\divamods"))
                Directory.CreateDirectory(acpath + "\\divamods");

            if (!Directory.Exists(acpath + "\\mdata\\M999"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999");

            if (!Directory.Exists(acpath + "\\mdata\\M999\\rom"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999\\rom");

            if (!Directory.Exists(acpath + "\\mdata\\M999\\rom\\auth_3d"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999\\rom\\auth_3d");

            if (!Directory.Exists(acpath + "\\mdata\\M999\\rom\\rob"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999\\rom\\rob");

            if (!Directory.Exists(acpath + "\\mdata\\M999\\rom\\script"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999\\rom\\script");

            if (!Directory.Exists(acpath + "\\mdata\\M999\\rom\\objset"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999\\rom\\objset");

            if (!Directory.Exists(acpath + "\\mdata\\M999"))
                Directory.CreateDirectory(acpath + "\\mdata\\M999");

            Logs.WriteLine("### MDATA LOAD ###");
            foreach (var i in mdataUtilities.getMdatas(acpath + @"\mdata"))
            {
                Logs.WriteLine("MDATA " + i);
            }
            foreach (mdataUtilities.dbType i in (mdataUtilities.dbType[])Enum.GetValues(typeof(mdataUtilities.dbType)))
                Logs.WriteLine(i.ToString() + " = " + mdataUtilities.getPath(acpath + @"\mdata", i));
            Logs.WriteLine("### MDATA DONE ###");

            objdb.Load(mdataUtilities.getPath(acpath + "\\mdata", mdataUtilities.dbType.obj_db));
            staged.Load(mdataUtilities.getPath(acpath + "\\mdata", mdataUtilities.dbType.stage_data));

            objdb2.Load((mdataUtilities.getPath(acpath + "\\mdata", mdataUtilities.dbType.obj_db_base)));
            staged2.Load((mdataUtilities.getPath(acpath + "\\mdata", mdataUtilities.dbType.stage_data_base)));



            //auth3d_db.load(a3ddbpath);
            auth3d_db.installmdata(mdataUtilities.getPath(acpath + "\\mdata", mdataUtilities.dbType.auth_3d_db));

            //dsc.ConvertDSC(path, acpath, divamods, false);
            //return;
            //auth3d_db.installmdata(Path.GetDirectoryName(a3ddbpath) + "\\mdata_auth_3d_db.bin");

            new Thread(() =>
            {
                Thread.CurrentThread.IsBackground = true;
                auth3d.ExtractA3D(path, acpath, auth3d_db);
                //GCLatencyMode oldMode = GCSettings.LatencyMode;
                //RuntimeHelpers.PrepareConstrainedRegions();
                try
                {
                    //GCSettings.LatencyMode = GCLatencyMode.LowLatency;
                    auth3d.ConvertA3D(path, acpath, auth3d_db);
                } finally
                {
                   //GCSettings.LatencyMode = oldMode;
                }

                GC.Collect();
                GC.WaitForPendingFinalizers();
                Interlocked.Decrement(ref currentWorker);
            }).Start();
            Interlocked.Increment(ref currentWorker);

            var texturedb = new TextureDatabase();
            texturedb.Load(mdataUtilities.getPath(acpath + "\\mdata", mdataUtilities.dbType.tex_db));

            var files = Directory.EnumerateFiles(path, "stgpv*.farc", SearchOption.TopDirectoryOnly).Concat(Directory.EnumerateFiles(path, "effpv*.farc", SearchOption.TopDirectoryOnly));

            filescount = files.Count();

            new Thread(() =>
            {
                while (filescount != 0)
                {
                    Console.Title = "LYB DIVA X2A - " + filescount + " objset left";
                    Thread.Sleep(500);
                }
                Console.Title = "LYB DIVA X2A";
            }).Start();

            new Thread(() =>
            {
                while (filescount != 0)
                {
                    if (model_list.Count() != 0)
                    {
                        models stgpv;
                        model_list.TryPop(out stgpv);
                        Console.WriteLine(Path.GetFileName(stgpv.fileName));
                        divax.GenerateObjSet(stgpv.fileName, stgpv.model, objdb, objdb2, texturedb, staged, staged2, acpath, divamods, true, false);
                        //GC.Collect();
                        //GC.WaitForPendingFinalizers();
                        Interlocked.Decrement(ref filescount);
                    }
                    Thread.Sleep(33);
                }
                Console.WriteLine("Stripifier : Done");
                Interlocked.Decrement(ref currentWorker);
            }).Start();
            Interlocked.Increment(ref currentWorker);

            foreach (string file in files)
            {
                while (currentWorker > maxWorker)
                {
                    Thread.Sleep(33);
                }
                /*
                bool stat = false;
                while ((Process.GetCurrentProcess().WorkingSet64 / (1024 * 1024)) > 1024)
                {
                    if (stat == false) Console.WriteLine("objset : PAUSING MODEL WORK");

                    if (stat == false)
                        stat = true;

                    Thread.Sleep(500);
                }
                if (stat == true)
                    Console.WriteLine("objset : CONTINUING WORK");
                */
                new Thread(() =>
                {
                    Thread.CurrentThread.IsBackground = true;
                    var le_model = divax.Stripify(file);
                    model_list.Push(le_model);
                    Interlocked.Decrement(ref currentWorker);
                }).Start();
                Interlocked.Increment(ref currentWorker);
            }

            while (filescount != 0)
            {
                Thread.Sleep(33);
            }
            Console.Title = "LYB DIVA X2A";

            texturedb.Save(acpath + @"\mdata\M999\rom\objset\mdata_tex_db.bin");

            model_list.Clear();
            
            /*
            foreach (string file in Directory.EnumerateFiles(path, "stgpv*.farc", SearchOption.TopDirectoryOnly))
            {
                Console.WriteLine(Path.GetFileName(file));
                var stgpv = model_list.Where(c => c.fileName == Path.GetFileName(path)).First();
                divax.GenerateObjSet(file, stgpv.model, objdb, texdbpath, staged, acpath, divamods, true, false);
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            foreach (string file in Directory.EnumerateFiles(path, "effpv*.farc", SearchOption.TopDirectoryOnly))
            {
                Console.WriteLine(Path.GetFileName(file));
                var stgpv = model_list.Where(c => c.fileName == Path.GetFileName(path)).First();
                divax.GenerateObjSet(file, stgpv.model, objdb, texdbpath, staged, acpath, divamods, true, false);
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            */

            while ((currentWorker != 0) || (currentWorkerA3DA != 0))
            {
                Console.Title = "LYB DIVA X2A - Awating pending worker... " + currentWorker + "|" + currentWorkerA3DA;
                Thread.Sleep(33);
            }

            Console.Title = "LYB DIVA X2A";
            GC.Collect();
            GC.WaitForPendingFinalizers();

            objdb.Save(acpath + @"\mdata\M999\rom\objset\mdata_obj_db.bin");
            staged.Save(acpath + @"\mdata\M999\rom\mdata_stage_data.bin");
            

            auth3d.CreateDBEntries(acpath, auth3d_db, divamods);

            auth3d_db.savemdata(acpath + @"\mdata\M999\rom\auth_3d\mdata_auth_3d_db.bin");

            if (skipmot)
            {
                motion.dbgme(path, acpath, divamods);
            } else
            {
                motion.Convert(path, acpath, mot_db, divamods);
            }

            divamods.Divamods.RemoveAll(c => c.pvid < 800);

            dsc.ConvertDSC(path, acpath, divamods, skippvparam, pvpp);

            divamods.Divamods.RemoveAll(c => c.pvid < 800);

            var csv = File.ReadAllLines("x_pv.csv");

            foreach (var i in csv)
            {
                var lines = i.Split(',');
                divamods.SetU1P2P(lines[0], lines[1], lines[2], lines[3], lines[4]);
            }

            divamods.GenerateDivamods(acpath);

            //divapvmods.ApplyMods(1,acpath, true);

            File.Copy(System.Reflection.Assembly.GetCallingAssembly().Location, acpath + @"\divamods_aft.exe", true);
            {
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.CreateNoWindow = false;
                startInfo.UseShellExecute = false;
                startInfo.FileName = acpath + "\\divamods_aft.exe";
                startInfo.WorkingDirectory = acpath + "\\";
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;

                using (Process exeProcess = Process.Start(startInfo))
                {
                    exeProcess.WaitForExit();
                }
            }

            stopWatch.Stop();
            // Get the elapsed time as a TimeSpan value.
            TimeSpan ts = stopWatch.Elapsed;
            //File.WriteAllBytes("x_cache",AnySerializer.Serializer.Serialize(divamods));

            Logs.WriteLine("DONE! took " + ts.TotalSeconds + " seconds.");

            Console.Beep();
            Console.ReadKey();
        }
    }
}
